package ar.edu.itba.pod.legajo45129.simul.communication;

import java.io.Serializable;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import org.apache.log4j.Logger;

import ar.edu.itba.pod.legajo45129.MessageProcessor;
import ar.edu.itba.pod.legajo45129.NodeId;
import ar.edu.itba.pod.legajo45129.simul.market.MarketManagerImpl;
import ar.edu.itba.pod.legajo45129.simul.simulation.SimulationManagerImpl;
import ar.edu.itba.pod.simul.communication.ClusterAdministration;
import ar.edu.itba.pod.simul.communication.ClusterCommunication;
import ar.edu.itba.pod.simul.communication.ConnectionManager;
import ar.edu.itba.pod.simul.communication.ReferenceName;
import ar.edu.itba.pod.simul.communication.SimulationCommunication;
import ar.edu.itba.pod.simul.communication.ThreePhaseCommit;
import ar.edu.itba.pod.simul.communication.Transactionable;
import ar.edu.itba.pod.simul.time.TimeMapper;

import com.google.common.base.Preconditions;

public class ConnectionManagerImpl implements ConnectionManager, Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private ClusterAdministration clusterAdministration;
	private ClusterCommunication clusterCommunication;
	
	private SimulationCommunication simulationCommunication;
	
	private NodeId nodeId;

	private TransactionManager transactionManager;

	private SimulationManagerImpl simulationManager;
	
	private MarketManagerImpl marketManager;
	
	
	private static Logger logger = Logger.getLogger(ConnectionManagerImpl.class);
	
	public ConnectionManagerImpl(String nodeId, TimeMapper timeMapper) throws RemoteException, AlreadyBoundException {
		UnicastRemoteObject.exportObject(this, 0);
		


		this.nodeId = new NodeId(nodeId);
		
		Registry registry;
		registry = LocateRegistry.createRegistry(this.nodeId.getPort());
		registry.rebind(ReferenceName.CONNECTION_MANAGER_NAME, this);
		
		logger.info("bind registry: " + this.nodeId);
		
		this.clusterAdministration = new ClusterAdministrationImpl(this);
		this.clusterCommunication = new ClusterCommunicationImpl(this);
		this.simulationManager = new SimulationManagerImpl(this, timeMapper);
		this.simulationCommunication = new SimulationCommunicationImpl(this);
		this.transactionManager = new TransactionManager(this);
		this.marketManager = new MarketManagerImpl(this);
		
		Thread thread = new Thread(MessageProcessor.getInstance(this));
		thread.start();
	}

	public ConnectionManagerImpl() throws RemoteException {
		UnicastRemoteObject.exportObject(this, 0);
	}

	@Override
	public ClusterAdministration getClusterAdmimnistration()
			throws RemoteException {
		return clusterAdministration;
	}

	@Override
	public int getClusterPort() throws RemoteException {
		return Registry.REGISTRY_PORT;
	}

	@Override
	public ConnectionManager getConnectionManager(String nodeId)
			throws RemoteException {
		Preconditions.checkNotNull(nodeId);
		logger.debug("getting connection manager of " + nodeId);
		NodeId node = new NodeId(nodeId);
		
		ConnectionManager cm = null;
		Registry registry = LocateRegistry.getRegistry(nodeId.toString());
//		final Registry registry = LocateRegistry.getRegistry(node.getIp(), node.getPort());
		try {
			cm = (ConnectionManager)registry.lookup(ReferenceName.CONNECTION_MANAGER_NAME);
			logger.debug("registry obtained");
		} catch (NotBoundException e) {
			logger.error("registry lookup error");
			throw new RemoteException(e.getMessage());
		}
		
		return cm;
	}

	@Override
	public ClusterCommunication getGroupCommunication() throws RemoteException {
		return this.clusterCommunication;
	}

	@Override
	public Transactionable getNodeCommunication() throws RemoteException {
		return transactionManager;
	}

	@Override
	public SimulationCommunication getSimulationCommunication()
			throws RemoteException {
		return this.simulationCommunication;
	}

	@Override
	public ThreePhaseCommit getThreePhaseCommit() throws RemoteException {
		return transactionManager.getThreepc();
	}

	public NodeId getNodeId() {
		return nodeId;
	}

	public SimulationManagerImpl getSimulationManager() {
		return simulationManager;
	}
	
	public MarketManagerImpl getMarketManager() {
		return marketManager;
	}
	
}
